#-*- coding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import keras
keras.__version__
from keras import models
from keras import layers
import numpy as np
from keras import backend as K
from keras.datasets import boston_housing

# 预测房子价格：一个回归的例子
# 注意，逻辑回归不是回归算法
# ## 波士顿房价数据集


(train_data, train_targets), (test_data, test_targets) =  boston_housing.load_data()
print"训练集的容量=",train_data.shape
print"测试集的容量=",test_data.shape

# 404条训练集
# 102条测试集
# 13个特征

# As you can see, we have 404 training samples and 102 test samples. The data comprises 13 features. 
# The 13 features in the input data are as follow:
# 
# 1. Per capita crime rate.人均财产犯罪率
# 2. Proportion of residential land zoned for lots over 25,000 square feet.住宅用地超过 25000 sq.ft. 的比例。
# 3. Proportion of non-retail business acres per town.城镇非零售商用土地的比例。(这个应该是个负面的因素)
# 4. Charles River dummy variable (= 1 if tract bounds river; 0 otherwise).查理斯河空变量（如果边界是河流，则为1；否则为0）。
# 5. Nitric oxides concentration (parts per 10 million).一氧化氮浓度。（这个是负面变量）
# 6. Average number of rooms per dwelling.住宅平均房间数。
# 7. Proportion of owner-occupied units built prior to 1940.1940 年之前建成的自用房屋比例。
# 8. Weighted distances to five Boston employment centres.到波士顿五个中心区域的加权距离。
# 9. Index of accessibility to radial highways.辐射性公路的接近指数。
# 10. Full-value property-tax rate per $10,000.每 10000 美元的全值财产税率。
# 11. Pupil-teacher ratio by town.每个镇（Town）上的小学老师比例
# 12. 1000 * (Bk - 0.63) ** 2 这里的Bk是黑人比例
# 13. % lower status of the population.人口中地位低下者的比例。
# 
# The targets are the median values of owner-occupied homes, in thousands of dollars:自住房的平均房价，以千美元计。（这个是预测目标）

 


train_targets


# 房价主要集中在10000美元～50000美元

# 准备数据
# 直接把这些范围不同的数据喂给神经网络是十分困难的。
# 神经王璐璐也许能自动适应这些各种各样范围的数据，
# 但是这会使得神经网络的学习十分的困难

# 一个更加普适的做法是把这些数据进行关于特征的标准化。
# 对于输入数据中的每个特征，我们抽取特征的平均值，并且除以标准差
#这样的话，这些特征的数值就会以0为中心，并且拥有一个单位的标准差。
# 这个用numpy很容易处理

 


mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std

test_data -= mean#这里是在进行标准化
test_data /= std

# 注意，这里的std是从train_data中来的，但是呢，从 train_data中计算得到的std被用到了test_data
# 中，所以作者说呢，实际工作中不要这么用（但是我认为这样其实是可以的）


# 建立我们的神经网络。
#因为数据量太少，所以我们使用一个很小的神经网络，只有两层隐藏层，每层64个隐藏单元。
# 使用小型的神经网络也是减缓过拟合的一种方式。

 



def build_model():
    # Because we will need to instantiate
    # the same model multiple times,
    # we use a function to construct it.
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu',
                           input_shape=(train_data.shape[1],)))
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model


# 我们的网络以单一的单元结束，没有激活函数 (i.e. it will be linear layer). 
# 这个是对于scalar regression（我们预测单一的连续的值）而言典型的设置


# 使用激活函数将会限制我们能输出的范围，
# 例如：
# 如果我们应用“sigmoid函数”到最后一层，那么这个网络就会只能预测出范围在[0,1]的值，
# 因为最后一层是纯线性的话，那么这个网络就能预测任何范围内的任何值。


# 注意，我们使用MSE来编译这个网络。

# 在训练期间，我们现在在监控一个新的机制：MAE
# 这个表示平均绝对误差，


# 使用K折验证来验证我们的方法
# 为了评价我们的神经网络，并保持调整它的参数（例如训练所使用的epoch的数量）
# 我们可以简单地把数据集分为训练集合和验证集

# 但是呢，由于数据集实在是太小了，所以不同的分割方式得到的验证集，用来对神经网络评价的结果会波动很大，
# 这个会妨碍我们对神经网络进行可靠的评估。

# 最佳方式是使用K折交叉验证
# 这个包含了把当前数据分成K份，然后初始化K个一模一样的模型，并把每个模型在K-1份数据上进行训练，
# 在剩余的那份数据上进行评估

# The validation score for the model used would 
# then be the average of the K validation scores obtained.

# 下面是很直接的代码

k = 4
num_val_samples = len(train_data) // k#地板除，也就是商舍掉小数点
# 这个意思是，每份数据集多大

num_epochs = 2#这里原来是100,为了加速，这里改为2
all_scores = []
for i in range(k):
    print"当前i=",i
    print('processing fold #', i)
    # Prepare the validation data: data from partition # k
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]#验证集的裸数据
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]#验证集的标签

    # 去掉上面的验证集，就是K折交叉验证中的训练集了。
    partial_train_data = np.concatenate(
        [train_data[:i * num_val_samples],
         train_data[(i + 1) * num_val_samples:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num_val_samples],
         train_targets[(i + 1) * num_val_samples:]],
        axis=0)

    # Build the Keras model (already compiled)
    model = build_model()#用来建立神经网络的具体模型
    # Train the model (in silent mode, verbose=0)
    model.fit(partial_train_data, partial_train_targets,epochs=num_epochs, batch_size=1, verbose=0)
    # Evaluate the model on the validation data
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    all_scores.append(val_mae)


print"所有评分：",all_scores

np.mean(all_scores)
print"test_data[0]=",test_data[0]
print"np.shape(test_data[0].reshape(1,13))=",np.shape(test_data[0].reshape(1,13))#这是一个102*13的输入维度。
# print"test_data[0]=",test_data[0]
# print"test_data[0]=",np.shape(np.array(test_data[0]).T)
# print"np.array(test_data[0]).T=",np.array(list[test_data[0]]).T
print"测试某条数据，结果为：",model.predict(test_data[0].reshape(1,13))

#-------------------------------------------------------------------
# 你可以看到，每个模型都会有不同的评分
# 让我们训练网络更多的epoch数



# Some memory clean-up
K.clear_session()



num_epochs = 20#这里原来是500,为了加速，改为2
all_mae_histories = []
for i in range(k):
    print('processing fold #', i)
    # Prepare the validation data: data from partition # k
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]

    # Prepare the training data: data from all other partitions
    partial_train_data = np.concatenate(
        [train_data[:i * num_val_samples],
         train_data[(i + 1) * num_val_samples:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num_val_samples],
         train_targets[(i + 1) * num_val_samples:]],
        axis=0)

    # Build the Keras model (already compiled)
    model = build_model()
    # Train the model (in silent mode, verbose=0)
    history = model.fit(partial_train_data, partial_train_targets,
                        validation_data=(val_data, val_targets),
                        epochs=num_epochs, batch_size=1, verbose=0)
    mae_history = history.history['val_mean_absolute_error']
    all_mae_histories.append(mae_history)


# We can then compute the average of the per-epoch MAE scores for all folds:



average_mae_history = [
    np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]


# Let's plot this:

import matplotlib.pyplot as plt

plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()


# 
# It may be a bit hard to see the plot due to scaling issues and relatively high variance. Let's:
# 
# * Omit the first 10 data points, which are on a different scale from the rest of the curve.
# * Replace each point with an exponential moving average of the previous points, to obtain a smooth curve.

 


def smooth_curve(points, factor=0.9):
  smoothed_points = []
  for point in points:
    if smoothed_points:
      previous = smoothed_points[-1]
      smoothed_points.append(previous * factor + point * (1 - factor))
    else:
      smoothed_points.append(point)
  return smoothed_points

smooth_mae_history = smooth_curve(average_mae_history[10:])

plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()


# 
# According to this plot, it seems that validation MAE stops improving significantly after 80 epochs. Past that point, we start overfitting.
# 
# Once we are done tuning other parameters of our model (besides the number of epochs, we could also adjust the size of the hidden layers), we 
# can train a final "production" model on all of the training data, with the best parameters, then look at its performance on the test data:

 

#------------------------以上是K折交叉验证在epoch=4或500的实验--------------------------------------------------------------------------
#-----------------------------下面是使用所有的训练集来进行训练--------------------------------
# Get a fresh, compiled model.
model = build_model()
# Train it on the entirety of the data.
model.fit(train_data, train_targets,
          epochs=80, batch_size=16, verbose=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)

print"test_mae_score=",test_mae_score




# We are still off by about \$2,550.

## Wrapping up
# 从这个实验中你应该学会的是：
# 回归使用和分类任务不同的loss函数。
# MSE是回归任务常用的loss函数
# 类似的，回归任务使用的评估机制也和分类任务不同，自然地，accuracy这个概念不适合回归任务
# 一个普遍的回归评估机制是MAE
# 当输入数据有不同的取值范围时，
# 每个特征应该在预处理阶段进行独立的放缩。
# 如果数据集的数量太少，使用k折交叉验证可以可靠地评价模型
# 如果数据集太少，那么最好使用一个比较小的神经网络模型以及比较少的隐藏层来避免严重的过拟合。（但是个人理解，其实欠拟合也很容易发生）
